﻿using K9Nano.Application.Dtos;
using K9Nano.Authorization;
using K9Nano.Domain.Authentication;
using K9Nano.Domain.Entities;
using K9Nano.Exceptions;
using K9Nano.GlobalOptions;
using K9Nano.Mapping;
using K9Nano.Session;
using Microsoft.EntityFrameworkCore;

namespace K9Nano.Application.Authentication;

public class UserAppServiceBase<TUser, TRole, TUserRole, TRolePermission, TDbContext, TCreateDto, TEditDto, TItemDto, TQueryDto, TUserManager>(TDbContext db, TUserManager userManager, IPermissionCatalog permissionCatalog)
    : CurdAppServiceBase<TUser, TDbContext, TCreateDto, TEditDto, TItemDto, TQueryDto>(db),
    IUserAppServiceBase<TUser, TCreateDto, TEditDto, TItemDto, TQueryDto>
    where TUser : UserBase
    where TUserRole : UserRoleBase<TUser, TRole>, new()
    where TRolePermission : RolePermissionBase<TRole>
    where TRole : RoleBase
    where TCreateDto : UserCreateDto
    where TEditDto : UserEditDto
    where TItemDto : UserDto
    where TQueryDto : UserQueryDto<TUser>
    where TDbContext : DbContext
    where TUserManager : IUserManager<TUser, TRole>
{
    /// <inheritdoc/>
    public override async Task<TItemDto> CreateAsync(TCreateDto createDto, CancellationToken cancellation)
    {
        var entity = MappingHelper.Mapper.Map<TUser>(createDto!);
        if (await db.Set<TUser>().AnyAsync(a => a.Username == entity.Username, cancellation))
        {
            throw FriendlyException.BadRequest($"用户名 {entity.Username} 已存在");
        }
        var user = await userManager.CreateAsync(entity, cancellation);

        if (createDto.Roles.Length > 0)
        {
            var ur = createDto.Roles.Select(a => new TUserRole { UserId = user.Id, RoleId = a });
            db.AddRange(ur);
        }
        var result = MappingHelper.Mapper.Map<TItemDto>(user);
        return result;
    }

    /// <inheritdoc/>
    public override async Task<TItemDto> UpdateAsync(TEditDto updateDto, CancellationToken cancellation)
    {
        var entity = await db.Set<TUser>().FirstOrDefaultAsync(a => a.Id.Equals(updateDto.Id), cancellation)
            ?? throw FriendlyException.BadRequest($"ID为 {updateDto.Id} 的用户不存在");

        if (updateDto.Username != entity.Username)
        {
            if (await db.Set<TUser>().AnyAsync(a => a.Username == updateDto.Username, cancellation))
            {
                throw FriendlyException.BadRequest($"用户名 {updateDto.Username} 已存在");
            }
        }

        entity = MappingHelper.Mapper.Map(updateDto, entity);
        await userManager.UpdateAsync(entity, cancellation);

        await db.Set<TUserRole>().Where(a => a.UserId == entity.Id).ExecuteDeleteAsync(CancellationToken.None);

        if (updateDto.Roles.Length > 0)
        {
            var roles = await db.Set<TRole>().AsNoTracking().Where(a => updateDto.Roles.Contains(a.Id)).ToListAsync(cancellation);
            if (roles.Count == 0) throw FriendlyException.BadRequest("角色参数无效");
            await userManager.AddRolesAsync(entity, roles, cancellation);
        }
        var item = MapToDto(entity);
        return item;
    }

    /// <inheritdoc/>
    public override async Task<bool> DeleteAsync(long id, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().FirstOrDefaultAsync(a => a.Id.Equals(id), cancellation);
        if (user is null) return true;
        return await userManager.DeleteAsync(user, cancellation);
    }

    /// <inheritdoc/>
    public virtual async Task AddRolesAsync(UserRoleEditDto dto, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().AsNoTracking().FirstOrDefaultAsync(a => a.Id.Equals(dto.UserId), cancellation)
                ?? throw FriendlyException.BadRequest($"ID为 {dto.UserId} 的用户不存在");
        var roles = await db.Set<TRole>().AsNoTracking().Where(a => dto.RoleIds.Contains(a.Id)).ToListAsync(cancellation);
        if (roles.Count == 0) throw FriendlyException.BadRequest("角色参数无效");
        await userManager.AddRolesAsync(user, roles, cancellation);
    }

    /// <inheritdoc/>
    public virtual async Task RemoveRolesAsync(UserRoleEditDto dto, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().AsNoTracking().FirstOrDefaultAsync(a => a.Id.Equals(dto.UserId), cancellation)
                 ?? throw FriendlyException.BadRequest($"ID为 {dto.UserId} 的用户不存在");
        var roles = await db.Set<TRole>().AsNoTracking().Where(a => dto.RoleIds.Contains(a.Id)).ToListAsync(cancellation);
        if (roles.Count == 0) throw FriendlyException.BadRequest("角色参数无效");
        await userManager.RemoveRolesAsync(user, roles, cancellation);
    }

    /// <inheritdoc/>
    public virtual async Task SetStatusAsync(UserStatusEditDto dto, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().AsNoTracking().FirstOrDefaultAsync(a => a.Id.Equals(dto.UserId), cancellation)
               ?? throw FriendlyException.BadRequest($"ID为 {dto.UserId} 的用户不存在");
        if (user.IsEnabled == dto.IsEnabled) return;
        await userManager.SetStatusAsync(user, dto.IsEnabled, cancellation);
    }

    /// <inheritdoc/>
    public virtual async Task<IEnumerable<Permission>> GetPermissionsByUserIdAsync(long userId, CancellationToken cancellation)
    {
        var isAdmin = await db.Set<TUserRole>().AsNoTracking()
            .AnyAsync(a => a.Role.Name == RoleNames.Admin, cancellation);
        if (isAdmin) return [];

        var roles = await db.Set<TUserRole>().AsNoTracking().Where(a => a.UserId == userId)
              .Select(a => a.RoleId)
              .ToListAsync(cancellation);
        if (roles.Count == 0)
        {
            return [];
        }

        var permissions = await db.Set<TRolePermission>().AsNoTracking()
            .Where(a => roles.Contains(a.RoleId))
            .Select(a => a.Permission)
            .ToListAsync(cancellation);
        if (permissions.Count == 0)
        {
            return [];
        }

        var result = permissionCatalog.Permissions.Where(a => permissions.Contains(a.Name, StringComparer.Ordinal)).ToList();
        return result;
    }

    /// <inheritdoc/>
    public virtual async Task<IEnumerable<RoleDto>> GetRolesByUserIdAsync(long userId, CancellationToken cancellation)
    {
        var roles = await db.Set<TUserRole>().Include(a => a.Role).AsNoTracking().Where(a => a.UserId == userId)
              .Select(a => a.Role)
              .ToListAsync(cancellation);

        var result = MappingHelper.Mapper.Map<List<RoleDto>>(roles);
        return result;
    }

    /// <summary>
    /// 为用户重置密码
    /// </summary>
    public virtual async Task<bool> ResetPasswordAsync(UserResetPasswordDto dto, CancellationToken cancellation)
    {
        var user = await db.Set<TUser>().FirstOrDefaultAsync(a => a.Id == dto.UserId, cancellation);
        if (user == null) return false;
        user.SetPassword(dto.Password);
        return await db.SaveChangesAsync(cancellation) > 0;
    }

    /// <inheritdoc/>
    public virtual async Task SetOrganizationAsync(ChangeUserOrganizationDto dto, CancellationToken cancellation)
    {
        await userManager.SetOrganizationAsync(dto.UserId, dto.OrganizationId, cancellation);
    }

    /// <inheritdoc/>
    public virtual async Task<TItemDto[]> GetUsersByOrganizantionIdAsync(long organizationId, CancellationToken cancellation)
    {
        var users = await db.Set<TUser>().AsNoTracking().Where(a => a.OrganizationId == organizationId).ToArrayAsync(cancellation);
        return users.Select(a => MappingHelper.Mapper.Map<TItemDto>(a)).ToArray();
    }
}
